﻿namespace Sobachka

#nowarn "40"

type StreamProcessor<'input, 'output> =
    | Put of (unit   -> 'output * StreamProcessor<'input, 'output>)
    | Get of ('input -> StreamProcessor<'input, 'output>)

module StreamProcessor =

    open Sobachka.Prelude
    
    // very basic

    let get = Get

    let put = Put

    let delay x sp = put (fun () -> x, sp)

    // combinators



    // primitives

    let rec id () = get (fun x -> delay x (id ()))

    let rec map f = get (fun x -> delay (f x) (map f))

    let rec filter p = get (fun x -> if p x then delay x (filter p)
                                            else filter p)

    // runners

    let applyToList sp xs =

        let rec applyToList' cont sp xs =
            match sp, xs with
            | Put f, xs -> 
                let x, sp' = f ()
                applyToList' (fun l -> cont (x::l)) sp' xs
            | Get f, x::xs    -> applyToList' cont (f x) xs
            | Get _, []       -> cont []

        applyToList' Operators.id sp xs


    let applyToSeq sp xs = 
        undefined ()
        

        //let rec applyToList' cont sp xs =
        //    match sp, xs with
        //    | Put f, xs -> 
        //        let x, sp' = f ()
        //        applyToList' (fun l -> cont (x::l)) sp' xs
        //    | Get f, x::xs    -> applyToList' cont (f x) xs
        //    | Get _, []       -> cont []

        //applyToList' Operators.id sp xs


    (* она же arr для стрелки.
    //let rec map f = get (fun x -> put ((f x) map ))
        //let rec map' = get (fun x -> put (f x) map')
        //map'

    let filter p = 
        let rec filter' = get (fun x -> if p x then put x filter'
                                               else filter')
        filter'

    let rec compose (p : StreamProcessor<'b, 'c>) (q : StreamProcessor<'a, 'b>) : StreamProcessor<'a, 'c> =
        match p, q with
        | Put f, _ -> put 
            //let x, p' = f ()
            //put x (compose p' q)
        | _, _ -> undefined ()

    
    // она же >>>
    let rec combine (p : unit -> StreamProcessor<'a, 'b>) (q : unit -> StreamProcessor<'b, 'c>) : unit -> StreamProcessor<'a, 'c> =
        //undefined ()
        //match p (), q () with
        //| p, Put (x, q)     -> put x (combine p q)
        //| Put (x, p), Get f -> combine p (f x)
        //| Get f, Get g      -> get (fun x -> let sp = f x in combine sp q)

    // ---------------
    
    let applyToList sp xs =

        let rec applyToList' cont sp xs =
            match sp, xs with
            | Put (o, sp), xs -> applyToList' (fun l -> cont (o::l)) sp xs
            | Get f, x::xs    -> applyToList' cont (f x) xs
            | Get _, []       -> cont []

        applyToList' Operators.id sp xs *)